
#ifndef TCP_CLIENT_IMPL_H
#define TCP_CLIENT_IMPL_H

#include <deque>
#include <boost/asio.hpp>
#include "tcp_message.hpp"
#include "messagebus.hpp"
#include <tuple>
#include <stdint.h>

using TcpMessageQueue = std::deque<TcpMessage>;
using ClientConfig_t = std::tuple<bool, int, int>;
using MessageType_t = std::map<std::string, int>;

class TcpClientImpl
{
public:
	TcpClientImpl(boost::asio::io_service& io_service,
		boost::asio::ip::tcp::resolver::iterator& endpoint_iterator,
		const ClientConfig_t& cfg,
		const MessageType_t& messageTypes)
		: _endpoint_iter(endpoint_iterator),
		_io_service(io_service),
		_socket(io_service),
		_heartbeatTimer(io_service),
		_reconnectTimer(io_service),
		_messageTypes(messageTypes)
	{
		loadConfig(cfg);
		start();
	}

	void write(TcpMessage& msg);
	void close();
	MessageBus<>& messagebus() { return _messagebus; }

private:

	void start();

	void loadConfig(const ClientConfig_t& cfg)
	{
		_isReconnect = std::get<0>(cfg);
		_reconnectInterval = std::get<1>(cfg) == 0 ? _reconnectInterval : std::get<1>(cfg);
		_heartbeatInterval = std::get<2>(cfg) == 0 ? _heartbeatInterval : std::get<2>(cfg);
	}

	void startConnect();

	void handleConnect(const boost::system::error_code& ec);

	void doReconnect(const boost::system::error_code& ec);
	void startRead();

	void handleReadHeader(const boost::system::error_code& ec);
	void handleReadBody(const boost::system::error_code& ec);
	void doWrite(TcpMessage& msg);

	void startWrite(const TcpMessage& msg);

	void handleWrite(const boost::system::error_code& ec);

	void checkHeartbeat();

	void doClose();

	void startReconnect();

	void init();
	void sendHeartbeatReq();

private:
	int _reconnectInterval = 5;
	int _heartbeatInterval = 10;
	bool _isReconnect = false;
private:
	bool _stopped = false;
	boost::asio::io_service& _io_service;
	boost::asio::ip::tcp::socket _socket;
	boost::asio::deadline_timer _heartbeatTimer;
	boost::asio::deadline_timer _reconnectTimer;

	TcpMessage _readMsg;
	TcpMessageQueue _writeMsgs;

	bool _isRecvedHeartbeatRsp = false;

    	uint32_t _currentSeq = 0;	// always increase
	uint32_t _heartBeatLostTimes = 0;

	std::string _rbuf;	// message bytes read buffer
	std::string _wbuf;	// message bytes write buffer

	boost::asio::ip::tcp::resolver::iterator& _endpoint_iter;
	MessageType_t _messageTypes;
	MessageBus<> _messagebus;
};

#endif
